﻿using Sidvall.Business;
using Sidvall.Net;
using Sidvall.Security;
using Sidvall.Serialization;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.DataPortal
{
    public class DataPortalStrategy : IDataPortalStrategy
    {
        #region Public Members

        public ServerConnection MasterServerConnection { get; set; }
        public IAuthenticationStrategy MasterAuthenticationStrategy { get; set; }
        public IServerConnectionProvider ServerConnectionProvider { get; set; }
        #region ServerConnections

        private System.Lazy<ServerConnectionCollection> _ServerConnections = new System.Lazy<ServerConnectionCollection>();
        public ServerConnectionCollection ServerConnections
        {
            get
            {
                return _ServerConnections.Value;
            }
        }

        #endregion

        #region RunLocal

        public bool RunLocal(string context)
        {
            var serverConnection = GetServerConnection(null, context);
            return RunLocal(serverConnection);
        }
        public bool RunLocal(IDataPortalContext dataPortalContext)
        {
            var serverConnection = GetServerConnection(dataPortalContext, null);
            return RunLocal(serverConnection);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public bool RunLocal(ServerConnection serverConnection)
        {
            if (serverConnection == null)
                throw new System.ArgumentNullException(nameof(serverConnection));
            return serverConnection.RunLocal;
        }

        #endregion

        /// <summary>
        /// Local
        /// </summary>
        #region GetTransactionScope

        public Sidvall.Data.ITransactionScope GetTransactionScope(string context)
        {
            var serverConnection = GetServerConnection(null, context);
            return GetTransactionScope(serverConnection);
        }
        public Sidvall.Data.ITransactionScope GetTransactionScope(IDataPortalContext dataPortalContext)
        {
            var serverConnection = GetServerConnection(dataPortalContext, null);
            return GetTransactionScope(serverConnection);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public Sidvall.Data.ITransactionScope GetTransactionScope(ServerConnection serverConnection)
        {
            if (serverConnection?.TransactionScopeFactory == null)
                return null;
            if (serverConnection.TransactionSettings != null)
                return serverConnection.TransactionScopeFactory.GetTransactionScope(serverConnection.TransactionSettings);
            else
                return serverConnection.TransactionScopeFactory.GetTransactionScope();
        }

        #endregion
        #region GetStorageManager

        public Sidvall.Data.IStorageManager GetStorageManager(string context)
        {
            var serverConnection = GetServerConnection(null, context);
            return GetStorageManager(serverConnection);
        }
        public Sidvall.Data.IStorageManager GetStorageManager(IDataPortalContext dataPortalContext)
        {
            var serverConnection = GetServerConnection(dataPortalContext, null);
            return GetStorageManager(serverConnection);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public Sidvall.Data.IStorageManager GetStorageManager(ServerConnection serverConnection)
        {
            if (serverConnection?.StorageManagerFactory == null)
                return null;
            return serverConnection.StorageManagerFactory.GetStorageManager(serverConnection.ConnectionString);
        }

        #endregion

        /// <summary>
        /// Remote
        /// </summary>
        #region GetWebAddress

        public string GetWebAddress(IDataPortalContext dataPortalContext, string path, string context)
        {
            var serverConnection = GetServerConnection(null, context);
            return GetWebAddress(serverConnection, path);
        }
        public string GetWebAddress(IDataPortalContext dataPortalContext, string path)
        {
            var serverConnection = GetServerConnection(dataPortalContext, null);
            return GetWebAddress(serverConnection, path);
        }
        public string GetWebAddress(ServerConnection serverConnection, string path)
        {
            return path;
        }

        #endregion
        #region GetHttpClient

        public Sidvall.Net.IHttpClient GetHttpClient(string context)
        {
            var serverConnection = GetServerConnection(null, context);
            return GetHttpClient(serverConnection);
        }
        public Sidvall.Net.IHttpClient GetHttpClient(IDataPortalContext dataPortalContext)
        {
            var serverConnection = GetServerConnection(dataPortalContext, null);
            return GetHttpClient(serverConnection);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public Sidvall.Net.IHttpClient GetHttpClient(ServerConnection serverConnection)
        {
            if (serverConnection?.HttpClientFactory == null)
                return null;
            var settings = new HttpClientSettings()
            {
                AuthenticationHeader = GetCurrentAuthenticationHeader(serverConnection),
                BaseAddress = serverConnection.RemoteAddress.Trim('/') + "/",
            };
            return serverConnection.HttpClientFactory.GetHttpClient(settings);
        }

        #endregion
        #region GetResultAsync

        public async Task<Sidvall.Net.HttpResponse> GetResultAsync(IDataPortalContext dataPortalContext, Sidvall.Net.IHttpClient client, string webAddress, string content)
        {
            var serverConnection = GetServerConnection(dataPortalContext, null);
            return await GetResultAsync(serverConnection, client, webAddress, content).ConfigureAwait(false);
        }
        public async Task<Sidvall.Net.HttpResponse> GetResultAsync(ServerConnection serverConnection, Sidvall.Net.IHttpClient client, string webAddress, string content)
        {
            var authenticationStrategy = GetAuthenticationStrategy(serverConnection);
            return await GetResultAsync(authenticationStrategy, client, webAddress, content).ConfigureAwait(false);
        }
        public async Task<Sidvall.Net.HttpResponse> GetResultAsync(IAuthenticationStrategy authenticationStrategy, Sidvall.Net.IHttpClient client, string webAddress, string content)
        {
            Sidvall.Net.HttpResponse item;

            if (content != null)
                item = await client.PostAsync(webAddress, content).ConfigureAwait(false);
            else
                item = await client.GetAsync(webAddress).ConfigureAwait(false);
            if (item.StatusCode == System.Net.HttpStatusCode.Unauthorized)
            {
                client.AuthenticationHeader = await GetNewAuthenticationHeaderAsync(authenticationStrategy).ConfigureAwait(false);
                if (client.AuthenticationHeader != null)
                {
                    if (content != null)
                        item = await client.PostAsync(webAddress, content).ConfigureAwait(false);
                    else
                        item = await client.GetAsync(webAddress).ConfigureAwait(false);
                }
            }
            return item;
        }

        #endregion

        /// <summary>
        /// Helper methods
        /// </summary>
        #region GetServerConnection

        public ServerConnection GetServerConnection(IDataPortalContext dataPortalContext, string context)
        {
            if (this.ServerConnectionProvider != null)
            {
                var item = this.ServerConnectionProvider.GetServerConnection(this, dataPortalContext, context);
                if (item != null)
                    return item;
            }
            if (this.MasterServerConnection != null)
                return this.MasterServerConnection;
            return this.ServerConnections.FirstOrDefault();
        }

        #endregion
        #region GetAuthenticationStrategy

        public IAuthenticationStrategy GetAuthenticationStrategy(ServerConnection serverConnection)
        {
            if (serverConnection?.AuthenticationStrategy != null)
                return serverConnection.AuthenticationStrategy;
            return this.MasterAuthenticationStrategy;
        }

        #endregion
        #region GetConnectionDescription

        public string GetConnectionDescription(string context)
        {
            var serverConnection = GetServerConnection(null, context);
            return GetConnectionDescription(serverConnection);
        }
        public string GetConnectionDescription(IDataPortalContext dataPortalContext)
        {
            var serverConnection = GetServerConnection(dataPortalContext, null);
            return GetConnectionDescription(serverConnection);
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public string GetConnectionDescription(ServerConnection serverConnection)
        {
            string[] items, parts;
            string result, server, database, itemValue;

            if (serverConnection == null)
                return null;
            if (!string.IsNullOrWhiteSpace(serverConnection.Description))
                return serverConnection.Description;
            switch (serverConnection.ConnectionTypeId)
            {
                case ConnectionType.FileStorage:
                    return serverConnection.ConnectionString;
                case ConnectionType.LocalSqlite:
                    return serverConnection.ConnectionString;
                case ConnectionType.NetworkSqlServer:
                    if (string.IsNullOrWhiteSpace(serverConnection.ConnectionString))
                        return string.Empty;
                    server = "";
                    database = "";
                    items = serverConnection.ConnectionString.Split(";", System.StringSplitOptions.RemoveEmptyEntries);
                    foreach (var item in items)
                    {
                        var pos = item.IndexOf("provider connection string=", System.StringComparison.OrdinalIgnoreCase);
                        if (pos >= 0)
                            itemValue = item.Substring(28);
                        else
                            itemValue = item;
                        parts = itemValue.Split("=");
                        if (parts[0].StartsWith("data source", System.StringComparison.OrdinalIgnoreCase))
                            server = parts[1];
                        else if (parts[0].StartsWith("server", System.StringComparison.OrdinalIgnoreCase))
                            server = parts[1];
                        else if (parts[0].StartsWith("initial catalog", System.StringComparison.OrdinalIgnoreCase))
                            database = parts[1];
                        else if (parts[0].StartsWith("database", System.StringComparison.OrdinalIgnoreCase))
                            database = parts[1];
                    }
                    result = server;
                    if (!string.IsNullOrWhiteSpace(database))
                    {
                        if (!string.IsNullOrWhiteSpace(server))
                            result += "/";
                        result += database;
                    }
                    return result;
                case ConnectionType.RemoteAccess:
                    return serverConnection.RemoteAddress;
                default:
                    return string.Empty;
            }
        }

        #endregion
        #region GetCurrentAuthenticationHeader

        public AuthenticationHeader GetCurrentAuthenticationHeader(ServerConnection serverConnection)
        {
            var authenticationStrategy = GetAuthenticationStrategy(serverConnection);
            if (authenticationStrategy != null)
                return authenticationStrategy.CurrentAuthenticationHeader;
            return null;
        }

        #endregion
        #region GetNewAuthenticationHeaderAsync

        public async Task<AuthenticationHeader> GetNewAuthenticationHeaderAsync(ServerConnection serverConnection)
        {
            var authenticationStrategy = GetAuthenticationStrategy(serverConnection);
            return await GetNewAuthenticationHeaderAsync(authenticationStrategy).ConfigureAwait(false);
        }
        public async Task<AuthenticationHeader> GetNewAuthenticationHeaderAsync(IAuthenticationStrategy authenticationStrategy)
        {
            if (authenticationStrategy != null)
            {
                if (await authenticationStrategy.NewAuthenticationHeaderFromRefreshTokenAsync().ConfigureAwait(false))
                    return authenticationStrategy.CurrentAuthenticationHeader;
            }
            return null;
        }

        #endregion

        #endregion
        #region Constructors

        public DataPortalStrategy()
        {
        }

        #endregion
    }
}
